package cn.zwk.handler.impl;

import cn.zwk.handler.Handler;
import cn.zwk.handler.model.HttpResult;
import cn.zwk.template.FreemarkerRenderer;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

@Slf4j
public class HttpHandler implements Handler {

    @Override
    public Object beforeHandle(Map<String, Object> envs, String key, String query, String[] row) {
        Objects.requireNonNull(envs, "envs");
        Map<String, Object> model = baseContext(envs, key, query, row);
        String method = str(envs.getOrDefault("method", "POST"));
        method = FreemarkerRenderer.renderString(method, model).toUpperCase(Locale.ROOT);
        String urlTmpl = str(envs.get("url"));
        if (urlTmpl == null || urlTmpl.isBlank()) {
            return new HttpResult(0, Map.of(), "Missing url template");
        }
        String url = FreemarkerRenderer.renderString(urlTmpl, model);
        String bodyTmpl = str(envs.get("body"));
        String body = bodyTmpl == null ? null : FreemarkerRenderer.renderString(bodyTmpl, model);

        Map<String, String> headers = headersFromEnv(envs, model);
        // 渲染 query Map（如果 before-handler 配置了 query: { k: v }）
        Map<String, String> queryMap = Map.of();
        Object q = envs.get("query");
        if (q instanceof Map<?, ?> qm) {
            queryMap = FreemarkerRenderer.renderMap(qm, model);
        }
        String timeoutTmpl = str(envs.getOrDefault("timeoutMs", "15000"));
        long timeoutMs = longVal(FreemarkerRenderer.renderString(timeoutTmpl, model));

        OkHttpClient client = new OkHttpClient.Builder()
                .followRedirects(true)
                .callTimeout(Duration.ofMillis(timeoutMs))
                .connectTimeout(Duration.ofMillis(timeoutMs))
                .readTimeout(Duration.ofMillis(timeoutMs))
                .writeTimeout(Duration.ofMillis(timeoutMs))
                .build();

        try {
            // 使用 HttpUrl 构建 URL 与查询参数（OkHttp 负责编码）
            String finalUrl = url;
            String lc = method.toUpperCase(Locale.ROOT);
            if (!queryMap.isEmpty()) {
                HttpUrl parsed = HttpUrl.parse(safeUrl(finalUrl));
                if (parsed != null) {
                    HttpUrl.Builder ub = parsed.newBuilder();
                    for (Map.Entry<String, String> e : queryMap.entrySet()) {
                        ub.addQueryParameter(e.getKey(), e.getValue());
                    }
                    finalUrl = ub.build().toString();
                }
            }

            Request.Builder rb = new Request.Builder().url(safeUrl(finalUrl));
            for (Map.Entry<String, String> e : headers.entrySet()) {
                rb.header(e.getKey(), e.getValue());
            }

//            Request request;
            Request request;
            // lc 已在上面定义
            if ("GET".equals(lc)) {
                request = rb.get().build();
            } else if ("DELETE".equals(lc)) {
                request = rb.delete().build();
            } else {
                String ct = headers.keySet().stream()
                        .filter(k -> k != null && k.equalsIgnoreCase("Content-Type"))
                        .findFirst().map(headers::get)
                        .orElse("application/json; charset=utf-8");
                MediaType mt = MediaType.parse(ct);
                RequestBody reqBody = RequestBody.create(body == null ? "" : body, mt);
                switch (lc) {
                    case "POST" -> request = rb.post(reqBody).build();
                    case "PUT" -> request = rb.put(reqBody).build();
                    case "PATCH" -> request = rb.patch(reqBody).build();
                    default -> request = rb.method(lc, reqBody).build();
                }
            }

            try (Response resp = client.newCall(request).execute()) {
                int code = resp.code();
                Map<String, String> flat = new HashMap<>();
                for (String name : resp.headers().names()) {
                    flat.put(name, String.join(",", resp.headers(name)));
                }
                String respBody = resp.body() != null ? resp.body().string() : "";
                if (code >= 400) {
                    log.warn("HTTP {} {} returned status {}, body: {}", method, finalUrl, code, respBody);
                }
                return new HttpResult(code, flat, respBody);
            }
        } catch (Exception e) {
            return new HttpResult(0, Map.of(), e.toString());
        }
    }

    private static String safeUrl(String url) {
        try {
            new java.net.URL(url);
            return url;
        } catch (Exception ex) {
            try {
                String trimmed = url.trim();
                int q = trimmed.indexOf('?');
                if (q < 0) {
                    return trimmed.replace(" ", "%20");
                }
                String base = trimmed.substring(0, q);
                String query = trimmed.substring(q + 1);
                String encoded = encodeQuery(query);
                return base + "?" + encoded;
            } catch (Exception e) {
                return url;
            }
        }
    }

    private static String encodeQuery(String query) {
        if (query == null || query.isEmpty()) return query;
        StringBuilder sb = new StringBuilder(query.length() + 16);
        String[] pairs = query.split("&");
        for (int i = 0; i < pairs.length; i++) {
            String p = pairs[i];
            int eq = p.indexOf('=');
            if (eq <= 0) {
                // 无等号或空键，原样保留（避免破坏调用者自定义语法）
                sb.append(encodeComponent(p));
            } else {
                String k = p.substring(0, eq);
                String v = p.substring(eq + 1);
                sb.append(encodeComponent(k));
                sb.append('=');
                sb.append(encodeComponent(v));
            }
            if (i < pairs.length - 1) sb.append('&');
        }
        return sb.toString();
    }

    private static String encodeComponent(String s) {
        try {
            return java.net.URLEncoder.encode(s, java.nio.charset.StandardCharsets.UTF_8)
                    .replace("+", "%20"); // 空格用%20
        } catch (Exception e) {
            return s;
        }
    }

    private static String encodeQueryMap(Map<String, String> map) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Map.Entry<String, String> e : map.entrySet()) {
            String k = e.getKey();
            String v = e.getValue();
            if (k == null) continue;
            if (!first) sb.append('&');
            sb.append(encodeComponent(k));
            sb.append('=');
            sb.append(encodeComponent(v == null ? "" : v));
            first = false;
        }
        return sb.toString();
    }

    private static String appendQuery(String url, String qs) {
        if (qs == null || qs.isEmpty()) return url;
        int q = url.indexOf('?');
        if (q < 0) return url + '?' + qs;
        if (q == url.length() - 1) return url + qs;
        if (url.endsWith("&")) return url + qs;
        return url + '&' + qs;
    }

    @Override
    public boolean handleStatus(Map<String, Object> envs, String key, String query, String[] row, Object res) {
        HttpResult r = (res instanceof HttpResult hr) ? hr : new HttpResult(0, Map.of(), String.valueOf(res));
        Map<String, Object> model = baseContext(envs, key, query, row);
        model.put("statusCode", r.getStatusCode());
        model.put("body", r.getBody());
        model.put("headers", r.getHeaders());
        String statusTpl = str(envs.get("statusTemplate"));
        if (statusTpl == null || statusTpl.isBlank()) {
            statusTpl = "<#if statusCode?number >= 200 && statusCode?number < 300>1<#else>0</#if>"; // 默认2xx成功
        }
        try {
            String rendered = FreemarkerRenderer.renderString(statusTpl, model);
            String norm = rendered == null ? "" : rendered.trim();
            return "1".equals(norm);
        } catch (RuntimeException e) {
            // 模板异常时采用默认规则
            return r.getStatusCode() >= 200 && r.getStatusCode() < 300;
        }
    }

    @Override
    public String handleSnippet(Map<String, Object> envs, String key, String query, String[] row, Object res) {
        HttpResult r = (res instanceof HttpResult hr) ? hr : new HttpResult(0, Map.of(), String.valueOf(res));
        String tmpl = str(envs.getOrDefault("snippetTemplate", "status=${statusCode} body=${(body!\"\")?truncate(120)}"));
        Map<String, Object> model = baseContext(envs, key, query, row);
        model.put("statusCode", r.getStatusCode());
        model.put("body", r.getBody());
        model.put("headers", r.getHeaders());
        r.getHeaders().forEach((k, v) -> model.put("header." + k, v));
        try {
            return FreemarkerRenderer.renderString(tmpl, model);
        } catch (RuntimeException e) {
            e.printStackTrace();
            String bodySafe = r.getBody() == null ? "" : (r.getBody().length() > 200 ? r.getBody().substring(0, 200) : r.getBody());
            return "status=" + r.getStatusCode() + " body=" + bodySafe;
        }
    }

    private static Map<String, String> headersFromEnv(Map<String, Object> envs, Map<String, Object> model) {
        Map<String, String> headers = new HashMap<>();
        Object h = envs.get("headers");
        if (h instanceof Map<?, ?> m) {
            headers.putAll(FreemarkerRenderer.renderMap(m, model));
        } else if (h instanceof String s) {
            String rendered = FreemarkerRenderer.renderString(s, model);
            String[] lines = rendered.split("\r?\n");
            for (String line : lines) {
                int idx = line.indexOf(":");
                if (idx > 0) {
                    String k = line.substring(0, idx).trim();
                    String v = line.substring(idx + 1).trim();
                    if (!k.isEmpty()) headers.put(k, v);
                }
            }
        }
        return headers;
    }

    private static String str(Object o) { return o == null ? null : String.valueOf(o); }
    private static long longVal(Object o) {
        if (o == null) return 0L;
        if (o instanceof Number n) return n.longValue();
        try { return Long.parseLong(String.valueOf(o)); } catch (Exception e) { return 0L; }
    }

    private static Map<String, Object> baseContext(Map<String, Object> envs, String key, String query, String[] row) {
        Map<String, Object> ctx = new HashMap<>();
        ctx.put("envs", envs);
        ctx.put("key", key);
        ctx.put("query", query);
        ctx.put("row", row == null ? List.of() : java.util.Arrays.asList(row));
        return ctx;
    }
    // no custom placeholder renderer; using Freemarker for all templates
}
